// $Id: gtkui.cpp 52 2007-05-30 16:34:55Z DarkAn9el $
/***************************************************************************
 *   Copyright (C) 2007 by Andrea Blankenstijn, Ulrich Giraud              *
 *   <darkan9el@gmail.com>, <blade.124@laposte.net>                        *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

#include <gtkmm.h>
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include "gtkui.h"
#include "../conversions.h"

/**
 * Constructeur de la classe, initialise toute la Gui
 */
GtkUi::GtkUi () :
  num_kbd(3,3,true), fnct_tbl(2,3,true)
{
  SetProperties();
  ArrangeBoxes();
  CreateKbd();
  CreateFnct();

  calc_entry.signal_activate().connect(sigc::mem_fun(*this,&GtkUi::ExecCalc));

  show_all_children();
}

/**
 * Permets de definir les propriétés par défaut des differents widgets
 */
void GtkUi::SetProperties() {
  set_title("Gtk abc ui");
  set_border_width(2);
  set_resizable(true);
  set_default_size(300,240);

  calc_entry.set_alignment(Gtk::ALIGN_RIGHT);
  calc_entry.set_text("0");

  frame_kbd.set_border_width(2);

}

/**
 * Permets d'ajouter les HBox et autres VBox dans la fenetre mère
 * et aussi d'ajouter les différents Widgets héritants de Gtk::Container
 * dans ces Box principales
 */
void GtkUi::ArrangeBoxes() {
  add(main_box);
  main_box.pack_start(calc_entry);
  main_box.pack_start(fnct_tbl);
  main_box.pack_start(frame_kbd);
  frame_kbd.add(num_kbd);
}

/**
 * Initialise le pavé numérique et les boutons opérateurs, ajoute
 * le tout dans un Gtk::Table et enfin connecte leurs signaux
 */
void GtkUi::CreateKbd() {
  char* str=(char*)malloc(sizeof(char)*2);

  for (int i = 0 ; i < 10 ; i++) {
    sprintf(str,"%d",i);
    nums_buttons[i] = new Gtk::Button((Glib::ustring)str,false);
    nums_buttons[i]->set_border_width(2);
    nums_buttons[i]->set_focus_on_click(false);
  }
  pi = new Gtk::Button("pi",false);
  point = new Gtk::Button(".",false);
  div = new Gtk::Button("/",false);
  mul = new Gtk::Button("x",false);
  min = new Gtk::Button("-",false);
  plus = new Gtk::Button("+",false);
  pi->set_border_width(2);
  pi->set_focus_on_click(false);
  point->set_border_width(2);
  point->set_focus_on_click(false);
  div->set_border_width(2);
  div->set_focus_on_click(false);
  mul->set_border_width(2);
  mul->set_focus_on_click(false);
  min->set_border_width(2);
  min->set_focus_on_click(false);
  plus->set_border_width(2);
  plus->set_focus_on_click(false);

  num_kbd.set_col_spacings(3);
  num_kbd.set_row_spacings(3);
  num_kbd.attach(*nums_buttons[7],0,1,0,1);
  num_kbd.attach(*nums_buttons[8],1,2,0,1);
  num_kbd.attach(*nums_buttons[9],2,3,0,1);
  num_kbd.attach(*nums_buttons[4],0,1,1,2);
  num_kbd.attach(*nums_buttons[5],1,2,1,2);
  num_kbd.attach(*nums_buttons[6],2,3,1,2);
  num_kbd.attach(*nums_buttons[1],0,1,2,3);
  num_kbd.attach(*nums_buttons[2],1,2,2,3);
  num_kbd.attach(*nums_buttons[3],2,3,2,3);
  num_kbd.attach(*nums_buttons[0],1,2,3,4);
  num_kbd.attach(*pi,2,3,3,4);
  num_kbd.attach(*point,0,1,3,4);
  num_kbd.attach(*div,3,4,0,1);
  num_kbd.attach(*mul,3,4,1,2);
  num_kbd.attach(*min,3,4,2,3);
  num_kbd.attach(*plus,3,4,3,4);

  for(int i=0; i<=9 ; i++) { // Connectes tout les boutons numeros
    nums_buttons[i]->signal_clicked().connect(sigc::bind<int>(sigc::mem_fun(*this,&GtkUi::KeyboardButtonClicked),i));
  }
  pi->signal_clicked().connect(sigc::bind<Glib::ustring>(sigc::mem_fun(*this,&GtkUi::ConnectWithText),"pi"));
  point->signal_clicked().connect(sigc::bind<Glib::ustring>(sigc::mem_fun(*this,&GtkUi::ConnectWithText),"."));
  div->signal_clicked().connect(sigc::bind<Glib::ustring>(sigc::mem_fun(*this,&GtkUi::ConnectWithText),"/"));
  mul->signal_clicked().connect(sigc::bind<Glib::ustring>(sigc::mem_fun(*this,&GtkUi::ConnectWithText),"*"));
  min->signal_clicked().connect(sigc::bind<Glib::ustring>(sigc::mem_fun(*this,&GtkUi::ConnectWithText),"-"));
  plus->signal_clicked().connect(sigc::bind<Glib::ustring>(sigc::mem_fun(*this,&GtkUi::ConnectWithText),"+"));
}

/**
 * Crée les boutons de fonctions specifiques comme ln( par exemple
 * Les arrange dans un Gtk::Table et connecte leurs signaux
 */
void GtkUi::CreateFnct() {
  int k=0;
  Glib::ustring func_array[12]={"^","(",")","sqrt(","sin(","cos(","tan(","log(","ln(","asin(","acos(","atan("};

  for( int i = 0 ; i <= 12 ; i++) {
    Functions[func_array[i]] = new Gtk::Button(func_array[i],false);
    Functions[func_array[i]]->signal_clicked().connect(sigc::bind<Glib::ustring>(sigc::mem_fun(*this,&GtkUi::ConnectWithText),func_array[i]));
    Functions[func_array[i]]->set_focus_on_click(false);
  }

  for( int i = 2 ; i >= 0 ; i-- )
    for( int j = 0 ; j <= 3 ; j++ )
      fnct_tbl.attach(*Functions[func_array[k++]],j,j+1,i,i+1);
}

/**
 * Fonction callback permettant de mettre a jour la Gtk::Entry
 * lorsqu'un bouton du clavier numérique est cliqué
 * \param[in] number le nombre a ajouter dans la Gtk::Entry
 */
void GtkUi::KeyboardButtonClicked(int number) {
  std::ostringstream oss; // Permets la conversion de int vers string de facon aisée
  Glib::ustring text=calc_entry.get_text();
  if (text=="0") oss << number;
  else oss << text << number;

  calc_entry.set_text((Glib::ustring)oss.str());
}

/**
 * Fonction callback permettant de mettre a jour la Gtk::Entry
 * lorsqu'un bouton contenant du texte est cliqué
 * \param[in] text Le text a ajouter a la Gtk::Entry
 */
void GtkUi::ConnectWithText(Glib::ustring text) {
  Glib::ustring entry_text=calc_entry.get_text();
  if (entry_text=="0") entry_text = text;
  else entry_text += text;

  calc_entry.set_text(entry_text);
}

/**
 * Fonction callback appliqué a la Gtk::Entry, permets la communication
 * du calcul vers les fonctions de main.cpp et conversions.cpp afin
 * d'executer le calcul et d'en afficher le résultat
 */
void GtkUi::ExecCalc() {
  Glib::ustring calc=calc_entry.get_text();
  const char* calc_ch=calc.c_str();
  std::ostringstream oss;
  Conversions bases;

  std::cin.putback('\n');
  for(int i=calc.length()-1; i>=0; i--)
  {
    std::cin.putback(calc_ch[i]);
  }
  get_token();
  if(ReadTable("obase") == 10)
    oss << expr(false);
  else
    oss << bases.DecToX(expr(false), (ushort) ReadTable("obase"));

  calc_entry.set_text(oss.str());
}
